package com.eclink.iot.base;

import com.eclink.iot.rfid.RFIDDeviceClient;
import com.eclink.iot.rfid.RFIDServer;
import com.eclink.iot.utils.ByteUtils;
import com.eclink.iot.utils.Log;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

import java.net.Socket;

@Slf4j
public abstract class DeviceBase
        extends SocketClientBase {

    protected static final int API_COMMAND_UPGRADE = 1;
    protected static final int API_COMMAND_GET_DEVICE_STATUS = 2;
    protected static final int API_COMMAND_REBOOT = 25;
    protected static final int API_COMMAND_ERROR = 255;
    protected static int API_FRAME_COMMAND = 0;
    protected static int API_FRAME_RESPONSE = 1;
    protected static int API_FRAME_NOTIFICATION = 2;

    protected static int API_ERRORCODE_SUCCESS = 0;
    protected static int API_ERRORCODE_CRC = 1;
    protected static int API_ERRORCODE_PARAMETER = 2;
    protected static int API_ERRORCODE_COMMAND = 23;

    private int api_rx_status = 0;
    private int api_rx_length = 0;
    private byte[] api_rx_buffer = new byte[2048];

    @Getter
    @Setter
    private int statusFailCount = 0;

    private UpgradeProcessListener upgrade_process_listener;

    private int api_errorcode = 255;
    private int api_upgrade_errorcode = 255;
    private int api_get_device_status_errorcode = 255;
    private int api_reboot_errorcode = 255;

    // 获取设备状态的失败次数
    private int get_device_status_failed_count;

    public void set_upgrade_process_listener(UpgradeProcessListener listener) {
        this.upgrade_process_listener = listener;
    }

    public Boolean upgrade(byte[] data) {
        try {
            int max_size = 512;
            int pack_count = data.length / max_size + ((data.length % max_size == 0) ? 0 : 1);
            long size = max_size;
            for (int i = 0; i < pack_count; i++) {
                Log.e(this.TAG, String.format("upgrade send buffer %d/ %d", new Object[]{Integer.valueOf(i + 1), Integer.valueOf(pack_count)}));
                int index = 0;
                byte[] buffer = new byte[1024];
                if (i == pack_count - 1) size = (data.length - i * max_size);
                buffer[index++] = (byte) (pack_count >> 8 & 0xFF);
                buffer[index++] = (byte) (pack_count & 0xFF);
                buffer[index++] = (byte) (i + 1 >> 8 & 0xFF);
                buffer[index++] = (byte) (i + 1 & 0xFF);
                for (int a = 0; a < size; a++) buffer[index++] = data[a + max_size * i];
                this.api_errorcode = 255;
                this.api_upgrade_errorcode = 255;
                api_package(API_FRAME_COMMAND, 1, ByteUtils.subString(buffer, 0, index));
                long time = System.currentTimeMillis();
                while (System.currentTimeMillis() - time < 5000L && this.api_upgrade_errorcode == 255 && this.api_errorcode == 255)
                    Thread.sleep(10L);
                if (this.api_upgrade_errorcode != API_ERRORCODE_SUCCESS || this.api_errorcode != 255)
                    return Boolean.valueOf(false);
                if (this.upgrade_process_listener != null)
                    this.upgrade_process_listener.notice((i + 1) / pack_count);
                Thread.sleep(20L);
            }
            return Boolean.valueOf(true);
        } catch (Exception ex) {
            return Boolean.valueOf(false);
        }
    }

    public DeviceBase(Socket socket) {
        super(socket);

        (new Thread(() -> {
            while (this.is_running) {
                try {
                    // 不停的请求设备的状态
                    get_device_status();
                    // 两秒请求一次
                    Thread.sleep(2000L);
                    Log.d(this.TAG, String.format("[%s] 设备获取状态成功! version: %s, runtime: %d", new Object[]{this.uuid, this.version, Integer.valueOf(this.runtime)}));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        })).start();
    }



    protected void concat_data(byte[] paramArrayOfByte, int length) {

        byte[] unpacked = new byte[length];
        System.arraycopy(paramArrayOfByte, 0, unpacked, 0, length);

        api_unpacking(unpacked);

//        for (int i = 0; i < length; i++) {
//            if (this.api_rx_status >= 2048) {
//                this.api_rx_status = 0;
//            }
//            this.api_rx_buffer[this.api_rx_status++] = buffer[i];
//
//            if (this.api_rx_status == 1) {
//                if ((this.api_rx_buffer[0] & 0xFF) != 170) {
//                    this.api_rx_status = 0;
//                }
//            } else if (this.api_rx_status == 5) {
//                this.api_rx_length = this.api_rx_buffer[3] << 8 & 0xFF00 | this.api_rx_buffer[4] & 0xFF;
//                this.api_rx_length += 7;
//            } else if (this.api_rx_status == this.api_rx_length) {
//                if ((this.api_rx_buffer[this.api_rx_status - 1] & 0xFF) == 221) {
//                    api_unpacking(ByteUtils.subString(this.api_rx_buffer, 0, this.api_rx_status));
//                    this.api_rx_status = 0;
//                } else {
//                    this.api_rx_status = 0;
//                }
//            }
//        }
    }

    public Boolean get_device_status() {
        try {
            api_package(API_FRAME_COMMAND, 2, new byte[0]);
            long time = System.currentTimeMillis();
            while (System.currentTimeMillis() - time < 5000L && this.api_get_device_status_errorcode == 255)
                Thread.sleep(10L);
            return Boolean.valueOf((this.api_get_device_status_errorcode == 0));
        } catch (Exception ex) {
            return Boolean.valueOf(false);
        }
    }

    public Boolean reboot() {
        try {
            this.api_reboot_errorcode = 255;
            api_build_command(25, new byte[0]);
            long time = System.currentTimeMillis();
            while (System.currentTimeMillis() - time < 5000L && this.api_reboot_errorcode == 255 && this.api_errorcode == 255)
                Thread.sleep(10L);
            if (this.api_errorcode != 255)
                return Boolean.valueOf(false);
            return Boolean.valueOf((this.api_reboot_errorcode == API_ERRORCODE_SUCCESS));
        } catch (Exception ex) {
            return Boolean.valueOf(false);
        }
    }

    private boolean api_package(int type, int command, byte... data) {
        int index = 0, check_sum = 0;
        byte[] buffer = new byte[2048];

        buffer[index++] = -86;
        buffer[index++] = (byte) type;
        buffer[index++] = (byte) command;
        buffer[index++] = (byte) (data.length >> 8 & 0xFF);
        buffer[index++] = (byte) (data.length & 0xFF);

        for (byte d : data) {
            buffer[index++] = d;
        }
        for (int i = 1; i < index; i++) {
            check_sum += buffer[i];
        }
        buffer[index++] = (byte) (check_sum & 0xFF);
        buffer[index++] = -35;

        return write(buffer, index);
    }

    protected boolean api_build_command(int command, byte... data) {
        return api_package(API_FRAME_COMMAND, command, data);
    }

    protected boolean api_build_response(int command, int errorcode) {
        return api_package(API_FRAME_RESPONSE, command, new byte[]{(byte) errorcode});
    }

    protected boolean api_response_success(int command) {
        return api_build_response(command, 0);
    }

    protected boolean api_response_failed(int errorcode) {
        return api_build_response(255, errorcode);
    }

    private void api_unpacking(byte[] buffer) {

        try {
            if (buffer == null) {
                return;
            }

            int crc = 0;
            for (int i = 1; i < buffer.length - 2; i++) {
                crc += buffer[i];
            }

            if ((crc & 0xFF) != (buffer[buffer.length - 2] & 0xFF)) {
                System.out.println("CRC 校验失败");
                return;
            }

            switch (buffer[2]) {

                case 1:
                    this.api_upgrade_errorcode = buffer[5];
                    return;
                case 2:

                    this.uuid = ByteUtils.toHexString(buffer, 6, 12, "");
                    this.version = ByteUtils.toAsciiString(buffer, 18, 24);
                    this.runtime = buffer[42] << 24 & 0xFF000000 | buffer[43] << 16 & 0xFF0000 | buffer[44] << 8 & 0xFF00 | buffer[45] & 0xFF;
                    byte status = buffer[5];

                    this.api_get_device_status_errorcode = buffer[5];
                    // 激活设备
                    RFIDServer.activeDevice(uuid, (RFIDDeviceClient) this);
                    return;

                case 25:
                    this.api_reboot_errorcode = buffer[5];
                    return;
            }

            command_receiving_notification(buffer[2], buffer);

        } catch (Exception ex) {
            Log.e(this.TAG, ex.getMessage());
        }
    }

    protected abstract void command_receiving_notification(int paramInt, byte[] paramArrayOfbyte);

    public static interface UpgradeProcessListener {
        void notice(double param1Double);
    }
}

